grammar ChatRules;

options
{
    language=C;
}

@header {
#include "ai.h"
}

/*------------------------------------------------------------------
 * PARSER RULES
 *------------------------------------------------------------------*/

definition[struct AIContext_struct *aiCtx] :
	(defaultCategoryElt[aiCtx] | genericCategory[aiCtx])* 
	;

defaultCategoryElt[struct AIContext_struct *aiCtx] :
	categoryElt[aiCtx]
	;

categoryElt[struct AIContext_struct *aiCtx] :
	(greeting[aiCtx] | rule[aiCtx] | fallback[aiCtx]) 
	;

genericCategory[struct AIContext_struct *aiCtx] :
	'category' STRING '{' { struct AICategory_struct *cat = aiAllocCategory(aiCtx, $STRING.text); aiCtx->currentCategory = cat; }
		categoryElt[aiCtx]+
	'}' { aiCtx->currentCategory = aiCtx->defaultCategory; }
	;
		
greeting[struct AIContext_struct *aiCtx] :
	'greetings' '=' '{'
		greetingString[aiCtx] (',' greetingString[aiCtx] )* ','? 
	'}'
	;

greetingString[struct AIContext_struct *aiCtx]
	@init {
		struct AIGreeting_struct *greeting = NULL; 
	}
	:
	STRING { greeting = aiAddGreeting(aiCtx, $STRING.text); } ('[' action[greeting->rule] ']')?
	;

			
fallback[struct AIContext_struct *aiCtx] : 
	'fallbacks' '=' '{'
		fallbackString[aiCtx] (',' fallbackString[aiCtx] )*	','? 
	'}'
	;

fallbackString[struct AIContext_struct *aiCtx]
	@init {
		struct AIFallback_struct *fallback = NULL; 
	}
	:
	STRING { fallback = aiAddFallback(aiCtx, $STRING.text); } ('[' action[fallback->rule] ']')?
	;

rule[struct AIContext_struct *aiCtx]
	@init {
		struct AIRule *pRule = aiAllocRule();
	}
	@after {
		aiAddRule(aiCtx, pRule);
	}
	:
	'if' expr=conditionalExpression[pRule] { pRule->expression = expr; }
	'{'
		action[pRule]
		(';'? action[pRule])*
		';'?
	'}'
	;
			
conditionalExpression[struct AIRule *pRule] returns [struct AIConditionalExpression *expr]
    :   ret=conditionalOrExpression[pRule] { $expr = ret;}
    ;

conditionalOrExpression[struct AIRule *pRule] returns [struct AIConditionalExpression *expr]
    :   e1=conditionalAndExpression[pRule] {e2 = NULL;} ( 'or' e2=conditionalAndExpression[pRule] )? { $expr = aiAllocOrExpression(e1, e2);}
    ;

conditionalAndExpression[struct AIRule *pRule] returns [struct AIConditionalExpression *expr]
    :   e1=unaryExpression[pRule] {e2 = NULL;} ( 'and' e2=unaryExpression[pRule] )?  { $expr = aiAllocAndExpression(e1, e2);}
    ;

unaryExpression[struct AIRule *pRule] returns [struct AIConditionalExpression *expr]
    :   'not' ret = unaryExpression[pRule] { $expr = aiAllocNotExpression(ret);}
    |   ret = primary[pRule] { $expr = ret;}
    ;

primary[struct AIRule *pRule] returns [struct AIConditionalExpression *expr]
    :   '(' ret = conditionalExpression[pRule] ')' { $expr = aiAllocParenthesisExpression(ret);}
		|	'contains' '(' STRING ')' { $expr = aiAllocContainsExpression($STRING.text);}
		|	'containss' '(' STRING ')' { $expr = aiAllocContainssExpression($STRING.text);}
		|	'starts' '(' STRING ')' { $expr = aiAllocStartsExpression($STRING.text);}
		|	'startss' '(' STRING ')' { $expr = aiAllocStartssExpression($STRING.text);}
		|	'remember' '(' STRING ')' { $expr = aiAllocRememberExpression($STRING.text);}
		|	'rememberas' '(' key=STRING ',' value=STRING ')' { $expr = aiAllocRememberAsExpression($key.text, $value.text);}
		;

stringExpression[struct AIRule *pRule] returns [struct AIStringExpression *expr]
    :   ret=concatenationExpression[pRule] { $expr = ret;}
    ;

concatenationExpression[struct AIRule *pRule] returns [struct AIStringExpression *expr]
    :   e1=primitiveExpression[pRule] {e2 = NULL;} ( '+' e2=primitiveExpression[pRule] )? { $expr = aiAllocConcatenationExpression(e1, e2);}
    ;

primitiveExpression[struct AIRule *pRule] returns [struct AIStringExpression *expr]
    :   '(' ret = stringExpression[pRule] ')' { $expr = aiAllocParenthesisStringExpression(ret);}
		| '[' NUMBER ']' { $expr = aiAllocInputExpression($NUMBER.text);}
		| STRING { $expr = aiAllocLiteralExpression($STRING.text);}
		| '%' ID { $expr = aiAllocTokenExpression($ID.text);}
		;

action[struct AIRule *pRule] 
		: 'reply' expr=stringExpression[pRule] { aiAddReplyAction(pRule, expr); }
		| 'category' expr=stringExpression[pRule] { aiAddCategoryAction(pRule, expr); }
		| 'save' STRING ',' expr=stringExpression[pRule]  { aiAddSaveAction(pRule, $STRING.text, expr); }
		;

/*------------------------------------------------------------------
 * LEXER RULES
 *------------------------------------------------------------------*/
COMMENT     : '#' ~'\n'* '\n' {$channel = HIDDEN;};

STRING    	:   '"'
				(~( '\\' | '"' | '\r' | '\n' ))* 
				'"'
			;
		

NUMBER	    : (DIGIT)+
            ;

ID	    	: (LETTER | DIGIT)+
            ;

WHITESPACE  : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+
              {
                 $channel = HIDDEN;
              }
            ;

fragment LETTER             : ('a'..'z' | 'A'..'Z');
fragment DIGIT              : ('0'..'9');
